package com.qigao.mall.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qigao.mall.api.*;
import com.qigao.mall.channel.dto.ChannelPayRespDto;
import com.qigao.mall.channel.dto.ChannelQueryRespDto;
import com.qigao.mall.commons.dto.*;
import com.qigao.mall.commons.enums.*;
import com.qigao.mall.commons.exceptions.BusinessException;
import com.qigao.mall.commons.param.ChannelRespParam;
import com.qigao.mall.commons.param.CommonReqParam;
import com.qigao.mall.commons.utils.DateUtil;
import com.qigao.mall.commons.utils.IDUtils;
import com.qigao.mall.jdbc.mapper.*;
import com.qigao.mall.jdbc.model.*;
import com.qigao.mall.service.channel.ChannelService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author luiz
 * @Title: com.qigao.mall.service.impl
 * @ProjectName three-mall
 * @date 2019-11-2019/11/8 16:53
 * @Description:
 */
@Service
public class PayServiceImpl implements PayService {
    private static final Logger logger= LoggerFactory.getLogger(PayServiceImpl.class);
    @Resource
    private TbMerProductMapper tbMerProductMapper;
    @Resource
    private TbShopInfoMapper tbShopInfoMapper;
    @Resource
    private ChannelRouteService channelRouteService;
    @Resource
    private TbPayDetailMapper tbPayDetailMapper;
    @Resource
    private TbOrderTotalMapper tbOrderTotalMapper;
    @Resource
    private TbItemMapper tbItemMapper;
    @Resource
    private ChannelService channelService;
    @Resource
    private AccountSerivce accountSerivce;
    @Resource
    private OpenShopService openShopService;
    @Resource
    private PromGroupService promGroupService;
    @Resource
    DataSourceTransactionManager transactionManager;
    @Override
    public  List<MerProductResultDto> findPayWayList(String shopNo, String webSource) {
      List<TbMerProduct> tbMerProducts =  tbMerProductMapper.selectEnableByShop(shopNo,webSource);
      if(tbMerProducts==null||tbMerProducts.size()<=0){
          //按照店铺查询不到，则取按照商户号查询的结果
          TbShopInfo tbShopInfo=tbShopInfoMapper.selectByShopNo(shopNo);
          tbMerProducts =  tbMerProductMapper.selectEnableByMerNo(tbShopInfo.getMerNo(),webSource);
      }
    if(tbMerProducts==null||tbMerProducts.size()<=0){
        return null;
    }
      List<MerProductResultDto> merProductResultDtos=new ArrayList<>();
       for (TbMerProduct tbMerProduct:tbMerProducts) {
           MerProductResultDto merProductResultDto=new MerProductResultDto();
           BeanUtils.copyProperties(tbMerProduct,merProductResultDto);
           merProductResultDtos.add(merProductResultDto);
       }
       return merProductResultDtos;
    }

    @Override
    public Result createPayOrder(PayCreateDto payCreateDto) {
        /**
         * 订单信息检查
         */
        TbOrderTotal tbOrderTotal=tbOrderTotalMapper.selectByOrderNo(payCreateDto.getOrderNo());
        if(tbOrderTotal==null || OrderStatusEnum.INIT.getCode()!=tbOrderTotal.getOrderStatus()){
            logger.info("订单{}不存在或者状态不是未支付状态",payCreateDto.getOrderNo());
            return Result.newError(ResultCode.OPER_NOT_ALlOWED);
        }
        //团购信息检查
        boolean payFlag=promGroupService.checkCanPay(payCreateDto.getOrderNo());
        if(!payFlag){
            logger.info("团购订单{}不允许支付",payCreateDto.getOrderNo());
            return Result.newError(ResultCode.OPER_NOT_ALlOWED);
        }
        TbMerChannel tbMerChannel=channelRouteService.findChannelRoute(tbOrderTotal.getMerNo(),payCreateDto.getWebSource(),payCreateDto.getPayWay());
        if(tbMerChannel==null){
            return Result.newError(ResultCode.CHANNEL_CONFIG_NOT_EXITS);
        }
        //1.记录付款订单信息
        TbPayDetail tbPayDetail=buildPayDetailInfo(payCreateDto,tbOrderTotal,tbMerChannel);
        tbPayDetailMapper.insertSelective(tbPayDetail);
        //2.向渠道发起支付请求
        MerChannelDto merChannelDto=buildMerchantDto(tbMerChannel,tbPayDetail);
        CommonReqParam commonReqParam=buildCommonParam(tbPayDetail,payCreateDto);
        ChannelRespParam channelRespParam=channelService.channelProcess(merChannelDto,commonReqParam);
        //4.返回支付链接或支付结果信息
        if(ChannelCodeEnum.SUCCESS.getCode().equals(channelRespParam.getRespCode())){
            //需要返回json对象
            if(StreamFormatEnum.JSON.equals(channelRespParam.getFormatEnum())){
                ChannelPayRespDto  channelPayRespDto=JSONObject.parseObject(channelRespParam.getRespContent(),ChannelPayRespDto.class);
                if(channelPayRespDto!=null&& YesNoEnum.YES.equals(channelPayRespDto.getIfSyn())){
                   //同步支付，更新支付信息
                    PayNotifyDto payNotifyDto = new PayNotifyDto();
                    payNotifyDto.setOutTradeNo(tbPayDetail.getPaySeqNo());
                    payNotifyDto.setTotalFee(channelPayRespDto.getTradeAmt());
                    payNotifyDto.setTimeEnd(channelPayRespDto.getTimeEnd());
                    payNotifyDto.setTransactionId(channelPayRespDto.getThirdTradeNo());
                    payNotifyDto.setTradeStatus(channelPayRespDto.getTradeStatus());
                    payNotifyDto.setTradeStateDesc(channelPayRespDto.getRetMsg());
                    payNotifyDto.setBuylerId(channelPayRespDto.getBuylerId());
                    payNotifyDto.setPaySuccessTime(channelPayRespDto.getPaySuccessTime());
                    processNotifyOrder(payNotifyDto);
                    channelRespParam.setRespContent("SUCCESS");
                }
            }
        }
        return Result.newSuccess(channelRespParam);
    }


     @Override
    public List<TbPayDetail> findUnPayList() {
        return tbPayDetailMapper.selectUnPayList();
    }

    @Override
    public List<TbPayDetail> findNotPayList() {
        return tbPayDetailMapper.selectNotPayList();
    }


    @Override
    public void retryQueryPay(TbPayDetail tbPayDetail) {
        //2.查询支付通道
        TbMerChannel tbMerChannel=channelRouteService.findChannelRoute(tbPayDetail.getMerNo(),tbPayDetail.getWebSource(),tbPayDetail.getPayWay());
        MerChannelDto merChannelDto=buildMerchantDto(tbMerChannel,tbPayDetail);
        merChannelDto.setChannelAction(ChannelActionEnum.ORDER_QEURY.getCode());
        CommonReqParam commonReqParam=new CommonReqParam();
        ChannelRespParam channelRespParam=channelService.channelProcess(merChannelDto,commonReqParam);
        TbPayDetail updatePayDetail=new TbPayDetail();
        updatePayDetail.setId(tbPayDetail.getId());
        if(ChannelCodeEnum.SUCCESS.getCode().equals(channelRespParam.getRespCode())){
            ChannelQueryRespDto channelQueryRespDto = JSON.parseObject(channelRespParam.getRespContent(), ChannelQueryRespDto.class);
            updatePayDetail.setPayStatus(channelQueryRespDto.getTradeStatus().getCode());
            updatePayDetail.setRetCode(channelQueryRespDto.getRetCode());
            updatePayDetail.setRetDesc(channelQueryRespDto.getRetMsg());
            PayStatusEnum tradeStatus= channelQueryRespDto.getTradeStatus();
            if(PayStatusEnum.PAY_SUCCESS.equals(tradeStatus)){
                PayNotifyDto payNotifyDto = new PayNotifyDto();
                payNotifyDto.setOutTradeNo(tbPayDetail.getPaySeqNo());
                payNotifyDto.setTotalFee(channelQueryRespDto.getTradeAmt());
                payNotifyDto.setTransactionId(channelQueryRespDto.getThirdTradeNo());
                payNotifyDto.setTradeStatus(tradeStatus);
                payNotifyDto.setTradeStateDesc(channelQueryRespDto.getRetMsg());
                payNotifyDto.setBuylerId(channelQueryRespDto.getBuylerId());
                //同步更新订单状态
                processNotifyOrder(payNotifyDto);
            }else if(PayStatusEnum.PAY_INIT.equals(tradeStatus)){
                //超过120分钟还未支付，则交易关闭。
                if(DateUtil.addMinute(tbPayDetail.getPayTime(),120).compareTo(new Date())<0){
                    updatePayDetail.setPayStatus(PayStatusEnum.PAY_CLOSE.getCode());
                }
            }
        }
        tbPayDetailMapper.updateRetry(updatePayDetail);
    }


    public void processNotifyOrder(PayNotifyDto payNotifyDto) {
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setName(TransactionTypeEnum.PAY_NOTIFY.getCode());
        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);
        try {
            if(!PayStatusEnum.PAY_SUCCESS.equals(payNotifyDto.getTradeStatus())){
                logger.info("[支付通知]支付订单不状态不是成功状态{},无需处理",payNotifyDto);
                return;
            }
            //1.更新支付明细表
            TbPayDetail tbPayDetail=tbPayDetailMapper.selectByPaySeqNo(payNotifyDto.getOutTradeNo());
            if(tbPayDetail==null||PayStatusEnum.PAY_INIT.getCode()!=tbPayDetail.getPayStatus()){
                logger.info("[支付通知]支付订单不存在或已经通知过，无须再次通知...");
                return;
            }
            TbPayDetail notifyTbPayDetail=new TbPayDetail();
            notifyTbPayDetail.setId(tbPayDetail.getId());
            notifyTbPayDetail.setVersion(tbPayDetail.getVersion());
            notifyTbPayDetail.setPaySeqNo(tbPayDetail.getPaySeqNo());
            notifyTbPayDetail.setPaySuccessTime(payNotifyDto.getPaySuccessTime());
            notifyTbPayDetail.setPayStatus(payNotifyDto.getTradeStatus().getCode());
            notifyTbPayDetail.setRespPayNo(payNotifyDto.getTransactionId());
            notifyTbPayDetail.setRetCode(payNotifyDto.getTradeState());
            notifyTbPayDetail.setRetDesc(payNotifyDto.getTradeStateDesc());
            BigDecimal thirdPayAmt=payNotifyDto.getTotalFee();
            notifyTbPayDetail.setBuyPayAmt(thirdPayAmt);
            notifyTbPayDetail.setThirdPayAmt(thirdPayAmt);
            notifyTbPayDetail.setBuylerId(payNotifyDto.getBuylerId());
            notifyTbPayDetail.setModiTime(new Date());
            int updateNum=tbPayDetailMapper.updateStatus(notifyTbPayDetail);

            if(updateNum==1){
                //2.更新订单表
                TbOrderTotal tbOrderTotal=new TbOrderTotal();
                tbOrderTotal.setOrderNo(tbPayDetail.getOrderNo());
                tbOrderTotal.setPayTime(new Date());
                tbOrderTotal.setOrderStatus(OrderStatusEnum.HAS_PAY.getCode());
                tbOrderTotal.setPayAmt(thirdPayAmt);
                tbOrderTotalMapper.updateByOrderNo(tbOrderTotal);
                //3.扣减库存
                tbItemMapper.subItemStockNum(tbPayDetail.getOrderNo());
                //=============后期需要调整为异步处理 Begin==========//
                //4.添加账户余额、记录账户日志、记录账户交易日志
               /* AccountOperDto accountOperDto =new AccountOperDto();
                accountOperDto.setMerNo(tbPayDetail.getMerNo());
                accountOperDto.setUserNo(tbPayDetail.getBuylerId());
                accountOperDto.setSourceOrderNo(tbPayDetail.getPaySeqNo());
                accountOperDto.setSourceTradeType(TradeTypeEnum.PAY);
                accountOperDto.setTradeAmt(thirdPayAmt);
                accountSerivce.trade(accountOperDto);*/
                //5.按照订单类型处理其他信息,例如:线上开店
                // openShopService.payNotify(tbPayDetail.getOrderNo());
                //6.添加团购信息
                promGroupService.payNotify(tbPayDetail.getPaySeqNo(),tbPayDetail.getPayAmt(),tbPayDetail.getOrderNo(),tbPayDetail.getBuylerId());
                //=============后期需要调整为异步处理 End==========//
            }
            transactionManager.commit(transactionStatus);
        }  catch (Exception e) {
            logger.error("[支付通知]发生异常,事物回滚:{}",  e);
            transactionManager.rollback(transactionStatus);
            throw new BusinessException(ResultCode.SAVE_DB_ERROR.getCode(), ResultCode.SAVE_DB_ERROR.getMessage());
        }
    }

    private CommonReqParam buildCommonParam(TbPayDetail tbPayDetail,PayCreateDto payCreateDto) {
        CommonReqParam commonReqParam= new CommonReqParam();
        commonReqParam.setMerNo(tbPayDetail.getMerNo());
        commonReqParam.setCharsetCode("UTF-8");
        commonReqParam.setRequestTime(DateUtil.getDateTimeFormat(new Date()));
        commonReqParam.setServiceName("");// todo
        commonReqParam.setTermIp(tbPayDetail.getEquipIp());
        commonReqParam.setNotifyUrl(tbPayDetail.getNotifyUrl());
        commonReqParam.setOpenId(payCreateDto.getOpenId());
        JSONObject reqContent=new JSONObject();
        reqContent.put("goodsName",tbPayDetail.getGoodsName());
        reqContent.put("startTime",tbPayDetail.getStartTime());
        reqContent.put("endTime",tbPayDetail.getEndTime());
        reqContent.put("startTimeStr",DateUtil.getDateFormat(tbPayDetail.getStartTime(),"yyyyMMddHHmmss"));
        reqContent.put("endTimeStr",DateUtil.getDateFormat(tbPayDetail.getEndTime(),"yyyyMMddHHmmss"));
        BigDecimal payAmt=tbPayDetail.getPayAmt();
        reqContent.put("payAmt",payAmt);
        BigDecimal payAmtFen=payAmt.multiply(new BigDecimal(100));
        reqContent.put("payAmtFen",payAmtFen.longValue());
        commonReqParam.setReqContent(reqContent.toString());
        return commonReqParam;
    }

    private MerChannelDto buildMerchantDto(TbMerChannel tbMerChannel, TbPayDetail tbPayDetail) {
        MerChannelDto merChannelDto= new MerChannelDto();
        merChannelDto.setMerNo(tbMerChannel.getMerNo());
        merChannelDto.setMerName(tbMerChannel.getMerName());
        merChannelDto.setChannelCode(tbMerChannel.getChannelCode());
        merChannelDto.setChannelAction(ChannelActionEnum.CREATE_ORDER.getCode());
        merChannelDto.setProductNo(tbMerChannel.getProductNo());
        merChannelDto.setAppId(tbMerChannel.getAppId());
        merChannelDto.setAppSecret(tbMerChannel.getAppSecret());
        merChannelDto.setChannelPartenerNo(tbMerChannel.getChannelPartenerNo());
        merChannelDto.setChannelPartenerName(tbMerChannel.getChannelPartenerName());
        merChannelDto.setSignType(tbMerChannel.getSignType());
        merChannelDto.setResvKey(tbMerChannel.getResvKey());
        merChannelDto.setMd5Key(tbMerChannel.getMd5Key());
        merChannelDto.setPrivKey(tbMerChannel.getPrivKey());
        merChannelDto.setPubKey(tbMerChannel.getPubKey());
        merChannelDto.setInnerSeqNo(tbPayDetail.getPaySeqNo());
        return merChannelDto;
    }

    private TbPayDetail buildPayDetailInfo(PayCreateDto payCreateDto, TbOrderTotal tbOrderTotal, TbMerChannel tbMerChannel) {
        TbPayDetail tbPayDetail=new TbPayDetail();
        tbPayDetail.setOrderNo(payCreateDto.getOrderNo());
        tbPayDetail.setPaySeqNo(IDUtils.genIdStr(IdTypeEnum.SEQ.getCode()));
        tbPayDetail.setPayTime(new Date());

        tbPayDetail.setPayAmt(tbOrderTotal.getActOrderAmt());
        tbPayDetail.setBuylerId(tbOrderTotal.getBuylerId());
        tbPayDetail.setBuylerName(tbOrderTotal.getBuylerName());
        tbPayDetail.setGoodsName(tbOrderTotal.getOrderDesc());
        tbPayDetail.setGoodsDesc(tbOrderTotal.getOrderDesc());
        tbPayDetail.setStartTime(new Date());
        tbPayDetail.setEndTime(DateUtil.addMinute(new Date(),120));//默认俩小时超时
        tbPayDetail.setEquipIp(payCreateDto.getReqTermIp());

        tbPayDetail.setMerNo(tbMerChannel.getMerNo());
        tbPayDetail.setMerName(tbMerChannel.getMerName());
        tbPayDetail.setPayWay(tbMerChannel.getPayWay());
        tbPayDetail.setPayWayName(tbMerChannel.getPayWayName());
        tbPayDetail.setWebSource(tbMerChannel.getWebSource());
        tbPayDetail.setWebSourceName(tbMerChannel.getWebSourceName());

        tbPayDetail.setChannelCode(tbMerChannel.getChannelCode());
        tbPayDetail.setChannelName(tbMerChannel.getChannelName());
        tbPayDetail.setChannelPartenerNo(tbMerChannel.getChannelPartenerNo());
        tbPayDetail.setChannelPartenerName(tbMerChannel.getChannelPartenerName());
        // todo
        return tbPayDetail;
    }
}
